//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2013 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_FUNCTORS_PREDICATES_UNARY_H_
#define _BUOLA_FUNCTORS_PREDICATES_UNARY_H_

#include <buola/buola.h>

namespace buola { namespace fn {

///\addtogroup functors
///@{

///\name Comparison (unary) predicates
//@{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// tag class for unary functions which can be used as predicates
///
/// This contains several function classes which can be used where a unary predicate is needed. Among others, it
/// contains functions to compare values to a constant.
///
/// It also contains boolean operators to combine the functions together.
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct GUnaryPredicate {};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// equal-to predicate
///
/// Evaluated with a single argument, returns true if the parameter is equal to the one given
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AEqualTo : public GUnaryPredicate
{
    ///initialises the comparison constant from its argument
    explicit AEqualTo(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit AEqualTo(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \c true if \p pVal is equal to \a mVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mVal==pVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// pointer-equal-to predicate
///
/// Evaluated with a single argument, returns true if the pointer (possibly smart pointer)
/// parameter is equal to the pointer given in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct APointerEqualTo : public GUnaryPredicate
{
    ///initialises the comparison constant from its argument
    explicit APointerEqualTo(tType *pVal)
        :   mVal(pVal)
    {}
    
    ///returns \c true if \p pVal is equal to \a mVal
    ///
    ///Before comparing, pVal is dereferenced, and then the address is taken again, to make sure
    ///that we have a raw pointer
    ///
    ///\todo Use std::addressof once it is implemented in libstdc++
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mVal==&*pVal;
    }
    
private:
    tType *mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// less-than predicate
///
/// Evaluated with a single argument, returns true if the parameter is less than the one given
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct ALessThan : public GUnaryPredicate
{
    ///initialises the comparison constant from its argument
    explicit ALessThan(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit ALessThan(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \c true if \p pVal is less than \a mVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return pVal<mVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// greater-than predicate
///
/// Evaluated with a single argument, returns true if the parameter is greater than the one given
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AGreaterThan : public GUnaryPredicate
{
    ///initialises the comparison constant from its argument
    explicit AGreaterThan(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit AGreaterThan(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \c true if \p pVal is greater than \a mVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mVal<pVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// in-range predicate
///
/// Evaluated with a single argument, returns true if the parameter is within the range given
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AInRange : public GUnaryPredicate
{
    ///initialises the comparison constants from its arguments
    template<typename t1,typename t2>
    AInRange(t1 &&p1,t2 &&p2)
        :   m1(std::forward<t1>(p1))
        ,   m2(std::forward<t2>(p2))
    {}
    
    ///returns \c true if \p pVal is between \a m1 and \a m2
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return !(pVal<m1||m2<pVal);
    }
    
private:
    tType m1,m2;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// is-any-of predicate
///
/// Evaluated with a single argument, returns true if the parameter is in the set specified in
/// the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AIsAnyOf : public GUnaryPredicate
{
    ///initializes the set from a null-terminated string
    explicit AIsAnyOf(const tType *pStr)
    {
        for(mSize=0;pStr[mSize];mSize++);
        
        tType *lStorage;
        
        if(UseFixedStorage())
        {
            lStorage=mStorage.mFix;
        }
        else
        {
            mStorage.mDyn=new tType[mSize];
            lStorage=mStorage.mDyn;
        }

        std::copy(pStr,pStr+mSize,lStorage);
        std::sort(lStorage,lStorage+mSize);
    }

    ///copy constructor
    AIsAnyOf(const AIsAnyOf &pO)
        :   mSize(pO.mSize)
    {
        tType *lStorage;
        tType *lOStorage;
        
        if(UseFixedStorage())
        {
            lStorage=mStorage.mFix;
            lOStorage=pO.mStorage.mFix;
        }
        else
        {
            mStorage.mDyn=new tType[mSize];
            lStorage=mStorage.mDyn;
            lOStorage=pO.mStorage.mDyn;
        }

        std::copy(lOStorage,lOStorage+mSize,lStorage);
    }

    ///move constructor
    AIsAnyOf(AIsAnyOf &&pO)
        :   mSize(pO.mSize)
    {
        if(UseFixedStorage())
        {
            std::copy(pO.mStorage.mFix,pO.mStorage.mFix+mSize,mStorage.mFix);
        }
        else
        {
            //just get the pointer from the source
            mStorage.mDyn=pO.mStorage.mDyn;
            pO.mStorage.mDyn=nullptr;
            pO.mSize=0; //so that the storage is not deleted
        }
    }

    ///destructor
    ~AIsAnyOf()
    {
        if(!UseFixedStorage())
            delete[] mStorage.mDyn;
    }

    // we don't want an assignment operator
    AIsAnyOf& operator=(const AIsAnyOf& pRH)=delete;

    ///returns true if \p pVal is in the set
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        const tType *lStorage=UseFixedStorage()?mStorage.mFix:mStorage.mDyn;

        return std::binary_search(lStorage,lStorage+mSize,pVal);
    }

private:
    ///returns true if the set fits into the fixed storage (at least 2 elements will always fit)
    bool UseFixedStorage() const
    {
        return mSize*sizeof(tType)<=sizeof(mStorage);
    }

    union
    {
        tType *mDyn;
        tType mFix[(sizeof(tType*)/sizeof(tType)<2)?2:(sizeof(tType*)/sizeof(tType))];
    } mStorage;

    std::size_t mSize; // number of elements
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// unary predicate OR
///
/// Combines two unary predicates into a single predicate, which is a logical \e OR of its two
/// arguments
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tLH,typename tRH>
struct AUnaryPredicateOr : public GUnaryPredicate
{
    ///initializes the sub-predicates from the arguments
    template<typename tALH,typename tARH>
    AUnaryPredicateOr(tALH &&pLH,tARH &&pRH)
        :   mLH(std::forward<tALH>(pLH))
        ,   mRH(std::forward<tARH>(pRH))
    {}
    
    ///returns \e true if either of the sub-predicates returns \e true for \p pVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mLH(pVal)||mRH(pVal);
    }
    
private:
    tLH mLH;
    tRH mRH;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// unary predicate AND
///
/// Combines two unary predicates into a single predicate, which is a logical \e AND of its two
/// arguments
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tLH,typename tRH>
struct AUnaryPredicateAnd : public GUnaryPredicate
{
    ///initializes the sub-predicates from the arguments
    template<typename tALH,typename tARH>
    AUnaryPredicateAnd(tALH &&pLH,tARH &&pRH)
        :   mLH(std::forward<tALH>(pLH))
        ,   mRH(std::forward<tARH>(pRH))
    {}
    
    ///returns \e true if both sub-predicates return \e true for \p pVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mLH(pVal)&&mRH(pVal);
    }
    
private:
    tLH mLH;
    tRH mRH;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// unary predicate NOT
///
/// Creates a new unary predicate, which is a logical \e NOT of its argument
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tSub>
struct AUnaryPredicateNot : public GUnaryPredicate
{
    ///initializes the sub-predicate from the argument
    explicit AUnaryPredicateNot(const tSub &pSub)
        :   mSub(pSub)
    {}
    
    ///initializes the sub-predicate from the argument (move version)
    explicit AUnaryPredicateNot(tSub &&pSub)
        :   mSub(std::move(pSub))
    {}
    
    ///returns \e true if the sub-predicate returns \e false for \p pVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return !mSub(pVal);
    }
    
private:
    tSub mSub;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// dereferencing unary predicate
///
/// Creates a new unary predicate, which dereferences its argument before passing it down
/// to the sub-predicate
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tSub>
struct AUnaryPredicateIndirect : public GUnaryPredicate
{
    ///initializes the sub-predicate from the arguments
    explicit AUnaryPredicateIndirect(const tSub &pSub)
        :   mSub(pSub)
    {}
    
    ///initializes the sub-predicate from the arguments (move version)
    explicit AUnaryPredicateIndirect(tSub &&pSub)
        :   mSub(std::move(pSub))
    {}
    
    ///returns \e true if the sub-predicate returns \e true for \p *pVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mSub(*pVal);
    }
    
private:
    tSub mSub;
};

///returns a predicate which is a logical \e OR of the arguments
template<typename tLH,typename tRH>
inline OEnableIfTag2<tLH,tRH,GUnaryPredicate,AUnaryPredicateOr<ODecay<tLH>,ODecay<tRH>>>
operator||(tLH &&pLH,tRH &&pRH)
{
    return AUnaryPredicateOr<ODecay<tLH>,ODecay<tRH>>(std::forward<tLH>(pLH),std::forward<tRH>(pRH));
}

///returns a predicate which is a logical \e AND of the arguments
template<typename tLH,typename tRH>
inline OEnableIfTag2<tLH,tRH,GUnaryPredicate,AUnaryPredicateAnd<ODecay<tLH>,ODecay<tRH>>>
operator&&(tLH &&pLH,tRH &&pRH)
{
    return AUnaryPredicateAnd<ODecay<tLH>,ODecay<tRH>>(std::forward<tLH>(pLH),std::forward<tRH>(pRH));
}

///returns a predicate which is a logical \e NOT of the argument
template<typename tSub>
inline OEnableIfTag<tSub,GUnaryPredicate,AUnaryPredicateNot<ODecay<tSub>>>
operator!(tSub &&pSub)
{
    return AUnaryPredicateNot<ODecay<tSub>>(std::forward<tSub>(pSub));
}

///returns a predicate which dereferences its argument before passing it to \p pSub
template<typename tSub>
inline OEnableIfTag<tSub,GUnaryPredicate,AUnaryPredicateIndirect<ODecay<tSub>>>
indirect(tSub &&pSub)
{
    return AUnaryPredicateIndirect<ODecay<tSub>>(std::forward<tSub>(pSub));
}

///returns a predicate which returns true if its argument is equal to \p pVal
template<typename tType>
inline AEqualTo<ODecay<tType>> equal_to(tType &&pVal)
{
    return AEqualTo<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a predicate which returns true if its argument is a pointer which is equal to \p pVal
///
///It only differs from equal_to in that it dereferences its argument and then takes the address again,
///so that it can be used to compare smart pointers with normal pointers.
template<typename tType>
inline APointerEqualTo<tType> pointer_equal_to(tType *pVal)
{
    return APointerEqualTo<tType>(pVal);
}

///returns a predicate which returns true if its argument is less than \p pVal
template<typename tType>
inline ALessThan<ODecay<tType>> less_than(tType &&pVal)
{
    return ALessThan<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a predicate which returns true if its argument is greater than \p pVal
template<typename tType>
inline AGreaterThan<ODecay<tType>> greater_than(tType &&pVal)
{
    return AGreaterThan<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a predicate which returns true if its argument is between \p p1 and \p p2
template<typename t1,typename t2>
inline AInRange<ODecay<t1>> in_range(t1 &&p1,t2 &&p2)
{
    return AInRange<ODecay<t1>>(std::forward<t1>(p1),std::forward<t2>(p2));
}

///returns a predicate which returns true if its argument is in the given set \p pSet
template<typename tType>
inline AIsAnyOf<tType> any_of(const tType *pSet)
{
    return AIsAnyOf<tType>(pSet);
}

//@}

///@}

/*namespace fn*/ } /*namespace buola*/ }

#endif
